﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

using mvcForum.Core;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core.Specifications;
using mvcForum.Web.Controllers;
using mvcForum.Web.Extensions;
using mvcForum.Web.Helpers;
using mvcForum.Web.ViewModels;
using mvcForum.Web.ViewModels.Delete;
using mvcForum.Web.ViewModels.Update;
using mvcForum.Web.Interfaces;
using System.Net.Mail;
using mvcForum.Core.Interfaces.Services;
using mvcForum.Core.Interfaces.Data;

namespace mvcForum.Web.Areas.Forum.Controllers {

	/// <summary>
	/// User Controller.
	/// The controller handles showing the user's public information.
	/// </summary>
	public class ProfileController : ThemedForumBaseController {
		private readonly IConfiguration config;
		private readonly IMembershipService membershipService;

		#region Constructors
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="config">Injected IConfiguration instance.</param>
		public ProfileController(IWebUserProvider userProvider, IContext context, IConfiguration config, IMembershipService membershipService)
			: base(userProvider, context) {

			// Store the configuration instance as a variable on the controller instance.
			this.config = config;
			this.membershipService = membershipService;
		}
		#endregion

		#region Read
		public ActionResult Index(Int32 id, String name) {
			try {
				// Get the user!
				ForumUser user = this.GetRepository<ForumUser>().Read(id);
				// Get the user's settings from the repository.
				//ForumUserSettings settings = this.GetRepository<ForumUserSettings>().ReadOne(new ForumUserSettingsSpecifications.SpecificUser(user));

				// Create the user model.
				UserViewModel model = new UserViewModel(user/*, settings*/);
				model.Path = new Dictionary<string, string>();

				return View(model);
			}
			catch { }

			// TODO: Handle the error, we need a common error page!!
			return View();
		}
		#endregion

		#region Update
		[Authorize]
		public ActionResult Update() {
			UpdateUserViewModel model = new UpdateUserViewModel(this.ActiveUser, this.ActiveUser.ExternalAccount, this.config.AllowUserDefinedTheme, this.Server.MapPath("~/themes"));
			return View(model);
		}

		[Authorize]
		[HttpPost]
		public ActionResult Update(UpdateUserViewModel model) {
			if (ModelState.IsValid) {
				String ns = "mvcForum.Web.Profile.Update";
				// Is this the actual current user?
				if (model.Id == this.ActiveUser.Id) {
					Boolean failed = false;
					if (this.ActiveUser.ExternalAccount) {
						// First visit ??
						if (this.ActiveUser.EmailAddress.ToLowerInvariant().EndsWith("repl@ce.this")) {
							// Yes, did the user remember to post a name?
							if (String.IsNullOrWhiteSpace(model.Name)) {
								// Nope!
								ModelState.AddModelError("Name", ForumHelper.GetString("MissingUsernameExternalUser", null, ns));
								failed = true;
							}
							else {
								// Yes, is the name allowed (not in use) ??
								IEnumerable<ForumUser> existingUsers = this.ForumUserRepository.ReadMany(new ForumUserSpecifications.SpecificUsername(model.Name));
								if (existingUsers.Any(fu => fu.Id != this.ActiveUser.Id)) {
									// Nope!
									ModelState.AddModelError("Name", ForumHelper.GetString("NameInUseExternalUser", null, ns));
									failed = true;
								}
							}
						}
						if (String.IsNullOrWhiteSpace(model.Email)) {
							ModelState.AddModelError("Email", ForumHelper.GetString("MissingEmailExternalUser", null, ns));
							failed = true;
						}
						else {
							try {
								MailAddress address = new MailAddress(model.Email);

								String existingUser = this.membershipService.GetAccountNameByEmailAddress(model.Email);
								if (!String.IsNullOrWhiteSpace(existingUser)) {
									ModelState.AddModelError("Email", ForumHelper.GetString("EmailInUse", null, ns));
									failed = true;
								}
							}
							catch {
								ModelState.AddModelError("Email", ForumHelper.GetString("InvalidEmailExternalUser", null, ns));
								failed = true;
							}
						}

						if (!failed) {
							IAccount account = this.membershipService.GetAccountByName(this.ControllerContext.RequestContext.HttpContext.User.Identity.Name, false);
							ForumUser user = this.ForumUserRepository.ReadOne(new ForumUserSpecifications.SpecificProviderUserKey(account.ProviderUserKey.ToString()));

							if (model.Email != account.EmailAddress) {
								account.EmailAddress = model.Email.ToLowerInvariant();
								user.EmailAddress = account.EmailAddress;
								// TODO: Send out e-mail?? To old and new?
								this.membershipService.UpdateAccount(account);
							}
							user.Name = model.Name;
							user.FullName = model.FullName;
							user.UseFullName = !String.IsNullOrWhiteSpace(model.FullName);
							user.Culture = model.Culture;
							user.Timezone = model.Timezone;
							if (this.config.AllowUserDefinedTheme) {
								user.Theme = model.Theme;
							}

							this.ForumUserRepository.Update(user);
							this.Context.SaveChanges();

							// The profile was updated, let's tell the user!
							TempData.Add("Status", ForumHelper.GetString("ChangesSaved", null, ns));
						}
					}
					else {
						Boolean oldPasswordGiven = false;

						// Did the user type in the existing password??
						if (!String.IsNullOrEmpty(model.OldPassword)) {
							// Yes, is it correct??
							oldPasswordGiven = this.membershipService.ValidateAccount(this.ControllerContext.RequestContext.HttpContext.User.Identity.Name, model.OldPassword);
						}

						IAccount account = this.membershipService.GetAccountByName(this.ControllerContext.RequestContext.HttpContext.User.Identity.Name, false);

						if (this.ActiveUser.EmailAddress != model.Email) {
							String existingUser = this.membershipService.GetAccountNameByEmailAddress(model.Email);
							if (!String.IsNullOrWhiteSpace(existingUser)) {
								ModelState.AddModelError("Email", ForumHelper.GetString("EmailInUse", null, ns));
								failed = true;
							}
						}

						// Was the e-mail address changed??
						if (this.ActiveUser.EmailAddress != model.Email) {
							// Yes, then the password is needed, was it given and correct?
							if (!oldPasswordGiven) {
								// No, let's stop then!
								ModelState.AddModelError("Email", ForumHelper.GetString("EmailChangeMissingPassword", null, ns));
								failed = true;
							}
							else {
								// Yes, e-mail address changed!
								account.EmailAddress = model.Email;
								// TODO: Send out e-mail?? To old and new?
							}
						}
						// TODO: Handled by the attributes on the model?
						if (!String.IsNullOrWhiteSpace(model.NewPassword) && model.NewPassword != model.RepeatedNewPassword) {
							ModelState.AddModelError("NewPassword", ForumHelper.GetString("PasswordsDoesNotMatch", null, ns));
							failed = true;
						}

						if (!String.IsNullOrWhiteSpace(model.NewPassword) && model.NewPassword == model.RepeatedNewPassword) {
							if (!oldPasswordGiven) {
								ModelState.AddModelError("Password", ForumHelper.GetString("OldPasswordsRequired", null, ns));
								failed = true;
							}
						}

						if (!failed) {
							try {
								if (!String.IsNullOrWhiteSpace(model.OldPassword) && !String.IsNullOrWhiteSpace(model.NewPassword)) {
									this.membershipService.ChangePassword(account, model.OldPassword, model.NewPassword);
								}

								this.membershipService.UpdateAccount(account);

								ForumUser user = this.ForumUserRepository.ReadOne(new ForumUserSpecifications.SpecificProviderUserKey(account.ProviderUserKey.ToString()));
								// TODO: If null ?!?!?!?!?!?
								if (user != null) {
									user.Culture = model.Culture;
									user.Timezone = model.Timezone;
									user.FullName = model.FullName;
									user.UseFullName = !String.IsNullOrWhiteSpace(user.FullName);
									user.EmailAddress = model.Email;
									// TODO: User flag etc etc etc.
									if (this.config.AllowUserDefinedTheme) {
										user.Theme = model.Theme;
									}
								}

								this.Context.SaveChanges();

								// The profile was updated, let's tell the user!
								TempData.Add("Status", ForumHelper.GetString("ChangesSaved", null, ns));
							}
							catch (FormatException) {
								ModelState.AddModelError("", ForumHelper.GetString("EditProfile.InvalidEmailAddress"));
							}
						}
					}
				}
			}

			model = new UpdateUserViewModel(this.ActiveUser, this.ActiveUser.ExternalAccount, this.config.AllowUserDefinedTheme, this.Server.MapPath("~/themes"));
			return View(model);
		}
		#endregion

		#region Delete
		[Authorize]
		public ActionResult Delete(Int32 id) {
			// TODO:
			DeleteUserViewModel model = new DeleteUserViewModel();
			return View(model);
		}

		[Authorize]
		[HttpPost]
		public ActionResult Delete(DeleteUserViewModel model) {
			if (model.Confirm) {
				// TODO:
				this.membershipService.DeleteAccount(this.ActiveUser.Name, true);
				// TODO:
				return Redirect("/");
			}
			return View(model);
		}
		#endregion
	}
}